﻿#include "Persistence.h"

#include "App/Window.h"
#include "Util/Compress.h"
#include "Util/Random.h"
#include "World/BlockRegistry.h"

Persistence::Persistence(const std::string &path)
{
  static std::string defaultPath = "saves/";
  static std::string defaultExt = ".world";

  m_path = defaultPath + path + defaultExt;
  m_seed = Util::RandUInt();
  m_camera = Camera::CreateDefault();
  m_camera->SetPosition({0, 100, 0});
  m_blockReg = CreateRef<BlockRegistry>();

  Load();

  m_blockReg->RegisterDefaultBlocks();
  m_camera->SetAspectRatio(Window::GetInstance().GetAspectRatio());
}

Persistence::~Persistence()
{
  Save();
}

void Persistence::CommitSeed(uint32_t seed)
{
  m_seed = seed;
}

void Persistence::CommitBlockRegistry(const Ref<BlockRegistry> &blockReg)
{
  m_blockReg = blockReg;
}

Ref<BlockRegistry> Persistence::GetBlockRegistry()
{
  return m_blockReg;
}

void Persistence::CommitCamera(const Ref<Camera> &camera)
{
  m_camera = camera;
}

Ref<Camera> Persistence::GetCamera()
{
  return m_camera;
}

void Persistence::CommitChunk(const Ref<Chunk> &chunk)
{
  chunks[chunk->GetPosition()] = chunk;
}

Ref<Chunk> Persistence::GetChunk(const glm::ivec2 &worldPosition)
{
  if (!chunks.contains(worldPosition))
  {
    return nullptr;
  }
  return chunks.at(worldPosition);
}

bool Persistence::Save()
{
  // 如果父目录不存在则创建
  const auto parentPath = std::filesystem::path(m_path).parent_path();
  std::filesystem::create_directories(std::filesystem::path(m_path).parent_path());

  std::ofstream file(m_path, std::ios::binary);
  if (!file.is_open())
    return false;

  LOG_INFO("Saving save file: {}...", m_path.c_str());

  const uint8_t version = SAVE_VERSION;
  file.write((char *)&version, sizeof(version));

  const int32_t seed = m_seed;
  file.write((char *)&seed, sizeof(seed));

  const float time = m_time;
  file.write((char *)&time, sizeof(time));

  const uint32_t blockRegMapSize = m_blockReg->m_blockNameToID.size();
  file.write((char *)&blockRegMapSize, sizeof(blockRegMapSize));
  for (const auto &[name, id] : m_blockReg->m_blockNameToID)
  {
    const uint32_t nameSize = name.size();
    file.write((char *)&nameSize, sizeof(nameSize));
    file.write((char *)&id, sizeof(id));
    file.write(name.c_str(), nameSize);
  }

  // 写入摄像机数据
  auto cameraPosition = m_camera->GetPosition();
  auto cameraRotation = m_camera->GetRotation();
  auto cameraFov = m_camera->GetFov();
  auto cameraNear = m_camera->GetZNear();
  auto cameraFar = m_camera->GetZFar();
  file.write((char *)(&cameraPosition), sizeof(cameraPosition));
  file.write((char *)(&cameraRotation), sizeof(cameraRotation));
  file.write((char *)(&cameraFov), sizeof(cameraFov));
  file.write((char *)(&cameraNear), sizeof(cameraNear));
  file.write((char *)(&cameraFar), sizeof(cameraFar));

  // 写入区块数量
  const uint32_t chunkCount = chunks.size();
  file.write((char *)&chunkCount, sizeof(chunkCount));

  for (auto &[position, chunk] : chunks)
  {
    file.write((char *)&position, sizeof(position));

    // 压缩方块数据
    std::vector<uint8_t> originData(Chunk::HorizontalSize * Chunk::HorizontalSize * Chunk::VerticalSize * sizeof(BlockData));
    // uint32_t originDataSize = 0;

    memcpy(originData.data(), &chunk->data, sizeof(chunk->data));

    // 压缩
    const uint32_t originDataSize = originData.size();
    file.write((char *)&originDataSize, sizeof(originDataSize));

    std::vector<uint8_t> compressedData;
    Util::Compress(originData, compressedData);
    const uint32_t compressedDataSize = compressedData.size();
    file.write((char *)&compressedDataSize, sizeof(compressedDataSize));
    file.write((char *)compressedData.data(), compressedDataSize);
  }
  return true;
}

bool Persistence::Load()
{
  // 读取版本号
  std::ifstream file(m_path, std::ios::binary);
  if (!file.is_open())
    return false;
  LOG_INFO("Loading save file: {}...", m_path.c_str());

  uint8_t version;
  file.read((char *)&version, sizeof(version));
  if (version != SAVE_VERSION)
    return false;

  // 读取种子
  file.read((char *)&m_seed, sizeof(m_seed));
  LOG_INFO("Read the world Sseed:{}", m_seed);

  // 读取时间
  file.read((char *)&m_time, sizeof(m_time));

  // 读取方块注册表
  uint32_t blockRegMapSize;
  file.read((char *)&blockRegMapSize, sizeof(blockRegMapSize));
  for (uint32_t i = 0; i < blockRegMapSize; i++)
  {
    uint32_t nameSize;
    file.read((char *)&nameSize, sizeof(nameSize));
    uint16_t id;
    file.read((char *)&id, sizeof(id));
    std::string name;
    name.resize(nameSize);
    file.read(name.data(), nameSize);
    m_blockReg->m_blockNameToID[name] = id;
  }

  // 读取摄像机
  glm::vec3 cameraPosition;
  glm::vec3 cameraRotation;
  float cameraFov;
  float cameraNear;
  float cameraFar;
  file.read((char *)&cameraPosition, sizeof(cameraPosition));
  file.read((char *)&cameraRotation, sizeof(cameraRotation));
  file.read((char *)&cameraFov, sizeof(cameraFov));
  file.read((char *)&cameraNear, sizeof(cameraNear));
  file.read((char *)&cameraFar, sizeof(cameraFar));
  m_camera->SetPosition(cameraPosition);
  m_camera->SetRotation(cameraRotation);
  m_camera->SetFov(cameraFov);
  m_camera->SetZNear(cameraNear);
  m_camera->SetZFar(cameraFar);

  // 读取区块数量
  uint32_t chunkCount;
  file.read((char *)&chunkCount, sizeof(chunkCount));

  // 读取区块数据
  for (uint32_t i = 0; i < chunkCount; i++)
  {
    glm::ivec2 chunkPosition;
    file.read((char *)&chunkPosition, sizeof(chunkPosition));

    uint32_t dataSize;
    file.read((char *)&dataSize, sizeof(dataSize));

    uint32_t compressedDataSize;
    file.read((char *)&compressedDataSize, sizeof(compressedDataSize));

    std::vector<uint8_t> compressedData(compressedDataSize);
    file.read((char *)compressedData.data(), compressedDataSize);

    std::vector<uint8_t> originData;
    Util::Decompress(compressedData, dataSize, originData);

    if (originData.size() != Chunk::HorizontalSize * Chunk::HorizontalSize * Chunk::VerticalSize * sizeof(BlockData))
      return false;

    Ref<Chunk> chunk = CreateRef<Chunk>(chunkPosition);
    memcpy(chunk->data, originData.data(), originData.size());

    chunks[chunkPosition] = chunk;
  }
  return true;
}
